package org.logsin37.tool.eurekamanager.domain.service.impl;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.logsin37.tool.eurekamanager.domain.entity.Eureka;
import org.logsin37.tool.eurekamanager.domain.entity.EurekaApplication;
import org.logsin37.tool.eurekamanager.domain.entity.EurekaInstance;
import org.logsin37.tool.eurekamanager.domain.service.EurekaRemoteDomainService;
import org.logsin37.tool.eurekamanager.infra.constant.ErrorMessages;
import org.logsin37.tool.eurekamanager.infra.constant.EurekaInstanceStatus;
import org.logsin37.tool.eurekamanager.infra.remote.EurekaRemoteClient;
import org.logsin37.tool.eurekamanager.infra.remote.impl.EurekaRemoteClientImpl;
import org.logsin37.tool.eurekamanager.infra.repository.EurekaRepository;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * Eureka Remote Domain Service Impl
 *
 * @author logsin37 2020/01/20 23:29
 */
@Service
public class EurekaRemoteDomainServiceImpl implements EurekaRemoteDomainService, InitializingBean {

    private final Map<String, EurekaRemoteClient> eurekaRemoteClientPool = new ConcurrentHashMap<>();

    private final EurekaRepository eurekaRepository;
    private final ObjectMapper objectMapper;

    public EurekaRemoteDomainServiceImpl(EurekaRepository eurekaRepository, ObjectMapper objectMapper) {
        this.eurekaRepository = eurekaRepository;
        this.objectMapper = objectMapper;
    }

    @Override
    public void afterPropertiesSet() {
        this.initEurekaRemoteClientPool();
    }

    private void initEurekaRemoteClientPool() {
        this.eurekaRepository.listAllEureka().subscribe(e -> this.register(e, true));
    }

    /**
     * 注册Eureka
     *
     * @param eureka eureka信息
     * @param errorOnRepeat 出现重复时是否报错
     */
    private void register(Eureka eureka, boolean errorOnRepeat) {
        Assert.notNull(eureka, String.format(ErrorMessages.NOT_NULL, "eureka info"));
        EurekaRemoteClient eurekaRemoteClient = new EurekaRemoteClientImpl(eureka, this.objectMapper);
        if(this.eurekaRemoteClientPool.get(eurekaRemoteClient.getEurekaCode()) != null && errorOnRepeat){
            throw new RuntimeException(String.format("Eureka Code %s is repeated", eurekaRemoteClient.getEurekaCode()));
        }
        this.eurekaRemoteClientPool.put(eurekaRemoteClient.getEurekaCode(), eurekaRemoteClient);
    }

    @Override
    public void register(Eureka eureka) {
        this.register(eureka, false);
    }

    @Override
    public EurekaRemoteClient unregister(String eurekaCode){
        final EurekaRemoteClient result = this.eurekaRemoteClientPool.get(eurekaCode);
        if(result != null){
            this.eurekaRemoteClientPool.remove(eurekaCode);
        }
        return null;
    }

    private EurekaRemoteClient getEurekaRemoteClientByCode(String eurekaCode){
        final EurekaRemoteClient result = this.eurekaRemoteClientPool.get(eurekaCode);
        if(result == null){
            throw new RuntimeException(String.format("Eureka client %s not exists", eurekaCode));
        }
        return result;
    }

    @Override
    public Flux<EurekaApplication> listAllApplications(String eurekaCode) {
        return this.getEurekaRemoteClientByCode(eurekaCode).listAllApplications();
    }

    @Override
    public Mono<Void> putStatus(String eurekaCode, EurekaInstance instance, EurekaInstanceStatus status) {
        return this.getEurekaRemoteClientByCode(eurekaCode).putStatus(instance, status);
    }

}
